package com.cc.springboot_test.config;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.google.common.collect.Lists;
import org.apache.commons.codec.Charsets;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class RestTemplateConfig {

    private static final Logger logger = LoggerFactory.getLogger(RestTemplateConfig.class);

    // 总最大连接数
    @Value("${org.apache.httpcomponents.maxConnectionsTotal:100}")
    private int maxConnectionsTotal = 1000;

    // 每个host的最大连接数
    @Value("${org.apache.httpcomponents.maxConnectionsPerHost:200}")
    private int maxConnectionsPerHost = 200;

    // 等待连接池管理器分配连接的最大时间，超过这个时间，说明系统繁忙
    @Value("${org.apache.httpcomponents.connManagerTimeout:2000}")
    private int connManagerTimeout = 2000;

    // 连接超时时间
    @Value("${http.connectTimeout:3000}")
    private int connTimeout = 3000;

    // 读取超时
    @Value("${http.readTimeout:10000}")
    private int soTimeout = 10000;

    // 重新验证连接是否可用时间间隔
    @Value("${org.apache.httpcomponents.inactivityInterval:1000}")
    private int inactivityInterval = 1000;

    @Bean(name = "httpClientConnectionManager")
    public PoolingHttpClientConnectionManager httpClientConnectionManager() {
        ConnectionConfig connConfig = ConnectionConfig.custom().setCharset(Charsets.UTF_8).build();
        SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();

        // 默认只支持http
        ConnectionSocketFactory plainsf = new PlainConnectionSocketFactory();
        RegistryBuilder<ConnectionSocketFactory> builder = RegistryBuilder.<ConnectionSocketFactory> create().register("http", plainsf);

        try {
            // 信任所有证书
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return false;
                }
            }).build();
            // 不验证域名与证书一致
            LayeredConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
            // 添加https支持
            builder.register("https", sslsf);
        } catch (Exception e) {
            logger.error("配置SSL异常", e);
        }
        Registry<ConnectionSocketFactory> r = builder.build();

        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r);
        cm.setMaxTotal(maxConnectionsTotal);
        cm.setDefaultMaxPerRoute(maxConnectionsPerHost);
        cm.setDefaultConnectionConfig(connConfig);
        cm.setDefaultSocketConfig(socketConfig);
        cm.setValidateAfterInactivity(inactivityInterval);
        return cm;
    }

    @Bean(name = "reqConfig")
    public RequestConfig reqConfig() {
        RequestConfig reqConfig = RequestConfig.custom()
                .setConnectTimeout(connTimeout)
                .setSocketTimeout(soTimeout)
                .setConnectionRequestTimeout(connManagerTimeout)
                .build();
        return reqConfig;
    }

    @Bean(name = "closeableHttpClient")
    public CloseableHttpClient apacheHttpClient() {
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(httpClientConnectionManager())
                .setDefaultRequestConfig(reqConfig())
                .build();
        return httpClient;
    }

    @Bean(name = "restTemplate")
    public RestTemplate restTemplate(@Autowired CloseableHttpClient closeableHttpClient) {
        RestTemplate restTemplate = new RestTemplate();

        // 解决中文乱码问题
        StringHttpMessageConverter converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        messageConverters.add(converter);

        FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter();
        List<MediaType> fastJsonMediaTypes = Lists.newArrayList();
        fastJsonMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastJsonConverter.setSupportedMediaTypes(fastJsonMediaTypes);
        messageConverters.add(fastJsonConverter);

        restTemplate.setMessageConverters(messageConverters);
        restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(closeableHttpClient));
        return restTemplate;
    }
}
